/*  SPDX-License-Identifier: GPL-2.0-or-later */
/*!********************************************************************

  Audacity: A Digital Audio Editor

  NetworkUtils.cpp

  Dmitry Vedenko

**********************************************************************/

#include "NetworkUtils.h"

#include <algorithm>

#include "OAuthService.h"
#include "Prefs.h"
#include "ServiceConfig.h"

#include "IResponse.h"
#include "Request.h"

#include "TranslatableString.h"

namespace audacity::cloud::audiocom {
using namespace audacity::network_manager;

namespace optional_headers {
const std::string InstanceId = "x-audacity-instance-id";
}

namespace {
SyncResultCode GetResultCodeFromHttpCode(int code) noexcept
{
    if (code == HttpCode::Conflict || code == HttpCode::UnprocessableEntity) {
        return SyncResultCode::Conflict;
    }

    if (code == HttpCode::PaymentRequired) {
        return SyncResultCode::PaymentRequired;
    }

    if (code == HttpCode::Unauthorized) {
        return SyncResultCode::Unauthorized;
    }

    if (code == HttpCode::Forbidden) {
        return SyncResultCode::Forbidden;
    }

    if (code == HttpCode::NotFound) {
        return SyncResultCode::NotFound;
    }

    if (code == HttpCode::PayloadTooLarge) {
        return SyncResultCode::TooLarge;
    }

    if (code == HttpCode::Gone) {
        return SyncResultCode::Expired;
    }

    if (code >= 500 && code < 600) {
        return SyncResultCode::InternalServerError;
    }

    return SyncResultCode::UnknownError;
}

SyncResultCode GuessResultCode(IResponse& response) noexcept
{
    const auto code = response.getError();

    if (code == NetworkError::NoError) {
        return SyncResultCode::Success;
    }

    if (code == NetworkError::OperationCancelled) {
        return SyncResultCode::Cancelled;
    }

    if (
        code == NetworkError::ConnectionFailed
        || code == NetworkError::ConnectionRefused
        || code == NetworkError::HostNotFound
        || code == NetworkError::ProxyConnectionFailed
        || code == NetworkError::ProxyNotFound
        || code == NetworkError::RemoteHostClosed
        || code == NetworkError::Timeout) {
        return SyncResultCode::ConnectionFailed;
    }

    if (code == NetworkError::HTTPError) {
        return GetResultCodeFromHttpCode(response.getHTTPCode());
    }

    return SyncResultCode::UnknownError;
}
} // namespace

ResponseResult GetResponseResult(IResponse& response, bool readBody)
{
    const auto resultCode = GuessResultCode(response);

    if (resultCode == SyncResultCode::Success) {
        return { resultCode,
                 readBody ? response.readAll<std::string>() : std::string {} }
    }

    if (response.getError() != NetworkError::HTTPError) {
        return { resultCode, response.getErrorString() }
    }

    return { resultCode,
             std::string("HTTP ") + std::to_string(response.getHTTPCode())
             + std::string("\n") + response.readAll<std::string>() };
}

void SetCommonHeaders(Request& request)
{
    const auto language = GetServiceConfig().GetAcceptLanguageValue();

    if (!language.empty()) {
        request.setHeader(
            audacity::network_manager::common_headers::AcceptLanguage, language);
    }

    auto& oauthService = GetOAuthService();

    if (oauthService.HasAccessToken()) {
        request.setHeader(
            common_headers::Authorization, oauthService.GetAccessToken());
    }
}

void SetOptionalHeaders(Request& request)
{
    if (SendAnonymousUsageInfo->Read()) {
        request.setHeader(
            optional_headers::InstanceId, InstanceId->Read().ToStdString());
    }
}

bool IsUploadRecoverable(SyncResultCode code)
{
    return code == SyncResultCode::Cancelled
           || code == SyncResultCode::ConnectionFailed
           || code == SyncResultCode::PaymentRequired
           || code == SyncResultCode::Unauthorized
           || code == SyncResultCode::Forbidden
           || code == SyncResultCode::InternalClientError
           || code == SyncResultCode::InternalServerError;
}

TransferStats& TransferStats::SetBytesTransferred(int64_t bytesTransferred)
{
    BytesTransferred = bytesTransferred;
    return *this;
}

TransferStats& TransferStats::SetBlocksTransferred(int64_t blocksTransferred)
{
    BlocksTransferred = blocksTransferred;
    return *this;
}

TransferStats&
TransferStats::SetProjectFilesTransferred(int64_t projectFilesTransferred)
{
    ProjectFilesTransferred = projectFilesTransferred;
    return *this;
}

TransferStats& TransferStats::SetTransferDuration(Duration transferDuration)
{
    TransferDuration = transferDuration;
    return *this;
}
} // namespace audacity::cloud::audiocom
